########################################################################
# 导入区
import json
import time

import sys
import pygame


from tkinter import Tk, messagebox, simpledialog   # 用于弹出提示框
Tk().wm_withdraw() #to hide the main window 

##################################################################################################
# 全局区
# 加载配置文件
with open('set.json', 'r', encoding='utf-8') as f:
	_set = f.read()
json_set = json.loads(_set)
HEIGHT = json_set['height']
WIDTH = json_set['width']
TITLE = json_set['title']
ICON = json_set['icon']    # 游戏窗口的图标
START_JSON = json_set['start_json']      # 游戏模式开始的json


# 下面是一些url路径(所有的路径只要是目录都要在结尾加上'/')
SAVE_PATH = 'save/'    # 存档的路径
DATA_PATH = 'data/'    # 游戏数据的存放目录

FONT_PATH = DATA_PATH + 'font/'   # 字体文件目录
ICON_PATH = DATA_PATH + 'icon/'   # ico文件目录
IMG_PATH = DATA_PATH + 'img/'     # 图片文件目录
MUSIC_PATH = DATA_PATH + 'music/' # 音乐文件目录
STORY_PATH = DATA_PATH + 'scene/' # 故事文件的目录

#######################################################################
# 功能性全局函数
def exit_game():   # 退出游戏
	pygame.quit()
	sys.exit()

#####################################################################
# 功能模块类
class Mz1Button:
	# 菜单界面的文字按钮类
	def __init__(self, text, x, y, common_color=(0, 0, 255), over_color=(255, 0, 0)):
		self.text_size = int(WIDTH / 27)
		self.text = text    # 按钮上显示的文字
		self.x = x
		self.y = y    # 在屏幕上的坐标
		self.rect = pygame.Rect(x, y, len(text)*self.text_size, self.text_size)
		self.font = pygame.font.Font(FONT_PATH+'a.ttf', self.text_size) # 加载生成一个字体
		
		self.common_color = common_color   # 正常时候的字体颜色
		self.over_color = over_color    # 鼠标在上面时候的字体颜色
		
		self.is_over = False    # 鼠标是否在控件区域内
		
	def show(self, screen):
		# 在屏幕上渲染当前控件
		self.check_is_over()   # 先检查鼠标是否在控件上方
		if self.is_over:
			t = self.font.render(self.text, True, self.over_color)  # 红色字符串
			screen.blit(t, (self.x, self.y))    # 渲染字符串
		else:
			t = self.font.render(self.text, True, self.common_color)  # 蓝色字符串
			screen.blit(t, (self.x, self.y))    # 渲染字符串
		
	def check_is_over(self):
		# 检测鼠标是不是在上方
		x, y = pygame.mouse.get_pos()
		self.is_over = self.rect.collidepoint((x, y))    # 看看鼠标在不在按钮区域内
		
	def on_click(self):
		if self.is_over:
			print('       > '+self.text+' 按钮被点击')
			return True
		else:
			return False


		


# Mz1Img类，用于显示图像和动画控制
# 属于一个基类，需要被继承
class Mz1Img:
	def __init__(self, img_url, x, y, width=None, height=None, alpha_rate=2, alpha_max=255, alpha=0):
		# print("> 初始化Mz1Img")
		self.alpha = alpha    # 不透明度
		self.alpha_max = alpha_max    # 不透明度最大值
		self.alpha_rate = alpha_rate   # 变化速率
		
		self.x = x
		self.y = y
		
		self.visible = True   # 是否可见
		
		self.img = pygame.image.load(IMG_PATH + img_url).convert_alpha()  # 加载(对话框)图片
		if width != None and height != None:   # 如果存在调整的尺寸 则调整
			self.img = pygame.transform.scale(self.img, (width, height))   # 转化(对话框)图片大小
		
		# self.json_data = json_data    # 获取json的数据
		
		# 控制动画的开关
		self.fade_in = True
		self.fase_out = False
		
	def show(self, screen):
		# 用于渲染显示图片
		if self.visible == True:
			self.img.set_alpha(self.alpha)    # 调整透明度
			screen.blit(self.img, (self.x,self.y))
		
	def update(self):
		# 用于更新图片动画
		
		if self.fade_in == True and self.alpha < self.alpha_max:   # fade_in 更新对话框透明度
			self.alpha += self.alpha_rate
			
		if self.fade_out == True and self.alpha > 0:    # fade_out   淡出对话框
			self.alpha -= self.alpha_rate
			
		
	def fade_out(self):
		# 使图片淡出
		self.fade_in = False  # 关掉fade_in开关
		self.fade_out = True   # 打开fade_out开关
		
	def move(self, x, y):
		# 移动图片
		self.x = x
		self.y = y
		
	def set_alpha(self, alpha):   # 设置图片透明度
		self.alpha = alpha
		
	def set_visible(self, visible):   # 设置是否可见
		self.visible = visible
		
	def set_size(self, width, height):   # 重新设置宽高
		self.img = pygame.transform.scale(self.img, (width, height))   # 转化图片大小
		
		

# 图像按钮类
# 继承Mz1Img类			
class Mz1ImgButton(Mz1Img):
	def __init__(self, img_url, x, y, width=None, height=None):
		super().__init__(img_url, x, y, width, height, alpha=255)   # 初始化就直接显示图标，不需要透明度变化
		self.rect = pygame.Rect(x, y, self.img.get_width(), self.img.get_height())  # 获取矩形区域
		self.is_over = False   # 初始化状态
		
		# 初始化透明度
		self.set_alpha(100)   # 100 or 255
		
	def check_is_over(self):
		# 检测鼠标是不是在上方
		x, y = pygame.mouse.get_pos()
		self.is_over = self.rect.collidepoint((x, y))    # 看看鼠标在不在按钮区域内
		
	def show(self, screen):
		# 先检测状态再显示
		self.check_is_over()   # 检测鼠标是否在上方
		if self.is_over:       # 变换图标的透明度
			self.set_alpha(255)
		else:
			self.set_alpha(100)
		super().show(screen)
		
	def on_click(self):
		if self.is_over:
			print('@图形按钮被点击')
			return True
		else:
			return False


	
# click_tip类，用于显示小图标提示
class Mz1ClickTip(Mz1Img):
	def __init__(self):
		super().__init__("click_tip.png", 0, 0, WIDTH*0.02, WIDTH*0.02, alpha_rate=8)
		self.visible = False   # 初始化为不可见
		
	def update(self):
		# 重载update方法
		super().update()   # 正常的动画更新
		# 循环播放动画效果
		if self.alpha >= self.alpha_max:
			self.fade_in = False
			self.fade_out = True
		
		if self.alpha <= 0:
			self.fade_in = True
			self.fade_out = False

	
# dialogue_box类，专门控制对话框图片
class Mz1DialogueBox(Mz1Img):
	def __init__(self):
		super().__init__("dialogue_box.png", 0, 0, WIDTH, HEIGHT, alpha_rate=2, alpha_max=190, alpha=0)
		print("  > 初始化Mz1DialogueBox")

	def reset(self):
		# 重置透明度，重新显示
		self.alpha = 0

# charactor_img类，专门控制人物的图片
class Mz1CharacterImg(Mz1Img):
	def __init__(self, img_url, name,  x, y, img_rate=None):
		#  (图片路径， 角色名， x, y)
		#   角色名用于判断是不是同一名角色，如果是就不需要切换效果
		super().__init__(img_url, x, y, alpha_rate=8)
		if img_rate == None:
			self.img_rate = 1    # 默认值为1，即图片直接是720p的
		else:
			self.img_rate = img_rate
		self.resize_character()     # 重新调整宽高，缩放人物图片
		
		
		self.x_max = x      # 设置到位的动画
		self.x = x - WIDTH*0.01     # 用于横向进入动画
		self.move_rate = 2   # 移动速率
		
		self.name = name   # 角色名
		print("  > 初始化Mz1CharacterImg")
		
	def resize_character(self):
		# 重设人物图片宽高
		width, height = self.img.get_size()
		print("    > 重设高宽："+str(self.img_rate))
		self.set_size(width*self.img_rate/1280*WIDTH, height*self.img_rate/720*HEIGHT)    # 重新调整宽高，以720p为标准的缩放
		
		
	def update(self):
		super().update()
		if self.x < self.x_max:
			self.x += self.move_rate   # 横向进入
	
	def change_img(self, img_url):
		# 用于在name相同的情况下更换角色图片而不加载动画
		self.img = pygame.image.load(IMG_PATH + img_url).convert_alpha()  # 加载(对话框)图片
		self.resize_character()
		
		

# 游戏模式的面板
class Mz1Game:
	# 真正的游戏模式
	def __init__(self):
		print('> 初始化Mz1Game')
		self.text_size = int(WIDTH / 42)      # 根据界面大小设置文本字体的大小
		self.font = pygame.font.Font(FONT_PATH+'a.ttf', self.text_size) # 生成一个字体
		
		self.json = START_JSON   # 设置要使用的scene json，初始化为set.json中指定的START_JSON
		
		# 一些基本参数
		self.end = False      # 游戏模块是否运行结束
		self.name_x = WIDTH * 0.2            # 名字显示的位置
		self.name_y = HEIGHT * 0.70
		self.text_x = WIDTH * 0.2            # 对话文字显示的位置
		self.text_y = HEIGHT * 0.75
		
		# 实例化对话框对象
		self.dialogue_box = Mz1DialogueBox()
		
		self.character_img = None   # 初始化角色图片对象
		
		# 实例化click_tip对象
		self.click_tip = Mz1ClickTip()
		
		# 创建底部菜单
		self.imgbtn_menu = Mz1ImgButton("imgbtn_menu.png", WIDTH*0.91, HEIGHT*0.9, WIDTH*0.035, WIDTH*0.035)    # 添加按钮
		self.imgbtn_save = Mz1ImgButton("imgbtn_save.png", WIDTH*0.95, HEIGHT*0.9, WIDTH*0.035, WIDTH*0.035)    # 添加按钮
		self.imgbtn_load = Mz1ImgButton("imgbtn_load.png", WIDTH*0.95, HEIGHT*0.82, WIDTH*0.035, WIDTH*0.035)    # 添加按钮
		self.imgbtn_exit = Mz1ImgButton("imgbtn_exit.png", WIDTH*0.91, HEIGHT*0.82, WIDTH*0.035, WIDTH*0.035)    # 添加按钮
		
		# 创建需要渲染的文字数组, 用于存放Mz1Text对象然后渲染
		self.text_list = []
		
		# 音乐对象
		self.voice_channel = pygame.mixer.Channel(1)
		self.bgm_channel = pygame.mixer.Channel(0)                  # 获取一个音频通道   0作为bgm通道  1作为人声音通道
		# self.bgm_channel.set_volume(0.1)   # 测试调节音量
		
		# self.bgm_channel.stop()         # 先关掉bgm
		
		self.load_scene()   # 初始化scene+加载ui
		self.load_dialogue()    # 加载第一个对话
		
	def is_end(self):         # 判断游戏是否结束了
		return self.end

	# 游戏场景加载和初始化模块
	def load_scene(self):
		# 加载一个scene， 初始化ui
		# 加载json 通过STORY_PATH来拼接
		with open(STORY_PATH + self.json, 'r', encoding='utf-8') as f:
			self.data = f.read()       # 加载游戏json模块 ./data/scene/...
		self.json_data = json.loads(self.data)
		self.dialogue = self.json_data['dialogue']
		
		self.dialogue_index = 0    # 对话的下标指针
		self.dialogue_length = len(self.dialogue)     # 总的对话长度
		
		self.say_index = 0     # 初始化说话(say)的index
		
		# 判断并加载bgm
		if self.json_data.get('bgm') != None:
			self.bgm = pygame.mixer.Sound(MUSIC_PATH+self.json_data.get('bgm'))  # 打开bgm文件
			self.bgm_channel.play(self.bgm, loops=-1)   # 循环播放
		
		# 判断是否有背景图，如果有就加载，如果没有的话就是黑屏
		if self.json_data.get('background') != None:
			self.game_img = pygame.image.load(IMG_PATH + self.json_data['background']).convert()   # 通过json加载游戏背景图片
			self.game_img = pygame.transform.scale(self.game_img, (WIDTH, HEIGHT))   # 转化图片大小
		else:
			self.game_img = None    # 设置背景为空
		
		self.bg_alpha = 0   # 背景透明度
		self.dialogue_box.reset()   # 重置对话框透明度
		

	def load_dialogue(self):
		# 用于加载当前dialogue_index的dialogue对象的函数 目前仅用于初始化load_scene的时候
		# 加载dialogue对象的人物图片
		if self.dialogue[self.dialogue_index].get('img') != None:    # 存在角色图片，则加载
			x = (self.dialogue[self.dialogue_index]['position'][0])*WIDTH  # 读取人物图片的位置
			y = (self.dialogue[self.dialogue_index]['position'][1])*HEIGHT
			self.character_img = Mz1CharacterImg(self.dialogue[self.dialogue_index].get('img'),
						self.dialogue[self.dialogue_index].get('who'), x, y, 
						self.dialogue[self.dialogue_index].get('img_rate'))   # 实例化人物图片对象
		else:	# 如果没有就置空
			self.character_img = None
			
		# 加载dialogue对象的文字
		self.text_list = []    # 清空需要渲染的字符串
		self.set_who = False   # 同于标志是否显示了人名
		self.which_to_say = 0     # 用于标志渲染了多少条 注意与say_index区分
		self.line = 0             # 渲染行号
		self.process_dialogue()   # 处理对话文字
			
		# 加载dialogue对象的声音
		if self.dialogue[self.dialogue_index].get('voice') != None:    # 若存在声音则播放
			self.voice_channel.play(pygame.mixer.Sound(MUSIC_PATH + self.dialogue[self.dialogue_index].get('voice')))


	def update(self):
		# 用于更新动画和游戏面板数据
		if self.bg_alpha < 255:   # 更新透明度
			self.bg_alpha += 5   # 测试用的透明度
			
		self.dialogue_box.update()    # 更新对话框(动画)
		self.click_tip.update()     # 更新点击提示（动画）
		
		for mztext in self.text_list:
			mztext.update()              # 更新文字动画
		
		if self.character_img != None:   # 存在人物图片，则更新
			self.character_img.update()    # 更新透明度
		
	def process_dialogue(self):
		# 专门用于处理对话框中文字的函数，在点击的时候进行调用	
		# ！！尚未解决重复添加的问题	
		who = self.dialogue[self.dialogue_index]['who']
		say = self.dialogue[self.dialogue_index]['say']      # 在新的版本中say改成了数组
		self.say_len = len(say)     # 获取说话的长度
		
		_say = say[:]   # 用于输出的_say，有时候需要重新整合
		if who != None:    # 如果存在说话的人
			if self.set_who == False:  #之前没有添加过人名
				# 将人名添加到渲染队列
				self.text_list.append(Mz1Text(who, self.name_x, self.name_y, color=(0, 0, 255)))
				self.set_who = True  # 修改标志
				
			# 存在说话的人则给说话的内容加上引号
			if self.say_len > 1:    # say列表长度大于1
				_say[0] = '「' + say[0]    # 加上引号
				_say[-1] = say[-1]+'」'    # 加上引号
			else:
				_say[0] = '「' + say[0] + '」'
				
		if self.dialogue[self.dialogue_index].get('auto_say') == True:   # 如果是auto_say,就直接显示所有的内容,缺省或者false则需要点击
			self.say_index = self.say_len - 1
			
		line = 0   # line 代表行号
		line = self.line   # 从上次渲染的开始
		for i in range(self.which_to_say, self.say_index+1):    # 显示_say的内容(从say转化后的内容)   一句一句的输出
			self.which_to_say += 1   # 将指针向后移动，这样下次就不会添加之前添加过的say到渲染列表
			s0 = _say[i]    # 存一个备份
			s1 = s0   # 先复制一下s
			s2 = ""    # 给一个空串
			# 进行分行判断和渲染
			while True:
				s_width, s_height = self.font.size(s1)    # 继续判断长度
				cut = 0   # cut标志剪切的位置
				while s_width > WIDTH*0.63:      # 根据s1判断是否要分行
					cut -= 1     # cut从-1开始剪
					s1 = s0[:cut]      # 截取部分
					s2 = s0[cut:]
					s_width, s_height = self.font.size(s1)    # 继续判断长度
				# 添加字符串到需要渲染的字符串序列中 测试
				self.text_list.append(Mz1Text(s1, self.text_x, self.text_y + line*(self.text_size*1.2)))
				if s2 == "":  # s2为空则表示没有发生裁剪，可以直接跳出循环
					break
				s1 = s2    # 将s2复制到s1再次进行之前的裁剪判断
				s0 = s2     # 将s2复制到s0作为标准串进行裁剪
				s2 = ""
				line += 1  # 行号加一，继续判断然后进行输出
			line += 1   # 默认的行号+1
		
		# 添加参数到全局，用于渲染点击提示
		self.line = line
		self.s_width = s_width
		self.s_height = s_height
		
		# 测试 输出text_list中的内容
		print("【text_list】")
		for i in self.text_list:
			print("     > "+i.s)
		print("【text_list over】")
		
	def show(self, screen):
		# 渲染游戏面板的函数，最终的一步渲染还是在主循环中完成的
		screen.fill((0,0,0))   # 输出黑屏
		
		if self.game_img != None:    # 若存在背景图则渲染
			self.game_img.set_alpha(self.bg_alpha)    # 调整透明度
			# print('当前透明度:'+ str(self.game_img.get_alpha()))
			screen.blit(self.game_img, (0,0))   # 渲染游戏背景图
		
		if self.character_img != None:    # 如果存在人物图片
			self.character_img.show(screen)    # 渲染显示人物图片


		# 渲染对话框
		self.dialogue_box.show(screen)   # 显示对话框
		
		# 渲染要显示的对话框中的文字(尚未完成)
		for mztext in self.text_list:
			mztext.show(screen)    # 显示


		# 渲染点击提示
		self.click_tip.move(self.text_x + self.s_width, self.text_y + (self.line-1)*self.s_height)   # 移动点击提示图标
		self.click_tip.set_visible(True)   # 设置点击提示为可见
		self.click_tip.show(screen)   # 显示点击提示
		
		# 渲染菜单/按钮
		self.imgbtn_menu.show(screen)
		self.imgbtn_save.show(screen)
		self.imgbtn_load.show(screen)
		self.imgbtn_exit.show(screen)

	def on_click(self, event):   # 面板被点击的处理函数, 同时接受event
		print(" > 捕获点击位置 "+str(event.pos))    # 点击事件的位置
		print(" > 捕获点击按钮 "+str(event.button))   # 点击事件的按钮

		_menu = self.imgbtn_menu.on_click()   # 检测按钮有没有被点击
		_save = self.imgbtn_save.on_click()   # 检测按钮有没有被点击
		_load = self.imgbtn_load.on_click()
		_exit = self.imgbtn_exit.on_click()
		if _menu:   # 返回菜单按钮被点击
			ans = messagebox.askokcancel("TIP", "确认要返回主菜单吗？")
			if ans == True:     # 确认要退出
				self.end = True    # 设置游戏结束
			return 0
		if _save:   # 保存按钮被点击
			ans = messagebox.askokcancel("TIP", "确认要存档吗？(将会覆盖之前的存档)")
			if ans == True:     # 确认
				#########################################
				# 存档功能模块(稍后要分离出来)
				_json = self.json    # 当前场景的json文件
				_dialogue_index = str(self.dialogue_index)     # 当前进行到的对话index
				with open(SAVE_PATH + 'save1.dat', 'w', encoding='utf-8') as f:
					f.write(_json)
					f.write('\n')
					f.write(_dialogue_index)				
				messagebox.showinfo("TIP", "存档成功")
			return 0  # 不处理下面的功能
			
		if _load:   # 读档按钮被点击
			ans = messagebox.askokcancel("TIP", "确认要读档吗？")
			if ans == True:     # 确认
				############################################
				# 读档功能模块
				with open(SAVE_PATH + 'save1.dat', 'r', encoding='utf-8') as f:
					_data = f.read()
				_data = _data.split('\n')   # 读取存档数据
				self.json = _data[0]      # 保存的场景
				self.load_scene()                # 加载这个json
				self.dialogue_index = int(_data[1])   # 保存的index
				self.load_dialogue()
				
				# self.dialogue_index = _data[1]    # 恢复之前的状态
			return 0  # 不处理下面的功能
			
		if _exit:   # 退出游戏按钮被点击
			ans = messagebox.askokcancel("TIP", "确认要退出游戏吗？")
			if ans == True:     # 确认要退出
				exit_game()
			return 0
			
		## 下面是没有点击按钮的处理
		if self.say_index < self.say_len - 1:     # 一次say没有结束
			self.say_index += 1    # 下次显示下一个say
			self.process_dialogue()    # 处理需要显示的字符串
			
		elif self.dialogue_index < self.dialogue_length - 1:       # 对话没有播放完, 继续博播放场景中的dialogue
			self.dialogue_index += 1      # 播放下一个dialogue
			self.say_index = 0     # 重置say_index
			
			self.text_list = []    # 清空需要渲染的字符串(有待测试)
			self.set_who = False    # 重置标志位，需要重新渲染人名
			self.which_to_say = 0       # 重置say要渲染的标志位
			self.line = 0   # 重置渲染行号
			self.process_dialogue()    # 处理需要显示的字符串
			
			# 加载下一个dialogue的数据
			# 加载下一个dialogue对象的人物图片
			if self.dialogue[self.dialogue_index].get('img') != None:    # 存在角色图片，则加载
				x = (self.dialogue[self.dialogue_index]['position'][0])*WIDTH  # 读取人物图片的位置
				y = (self.dialogue[self.dialogue_index]['position'][1])*HEIGHT
				# 判断是不是同一个人，如果是就直接换图片不需要更新动画了
				if self.character_img != None and self.character_img.name == self.dialogue[self.dialogue_index].get('who'):    # 判断为同一个人说话的人
					# 直接换图片而不加载动画
					self.character_img.change_img(self.dialogue[self.dialogue_index].get('img'))  # 更换图片
				else:   # 否则就重新加载一个CharacterImg实例
					self.character_img = Mz1CharacterImg(self.dialogue[self.dialogue_index].get('img'),
									self.dialogue[self.dialogue_index].get('who'), x, y, 
									self.dialogue[self.dialogue_index].get('img_rate'))   # 实例化人物图片对象
			else:
				self.character_img = None   # 设置为空
			# 加载声音
			if self.dialogue[self.dialogue_index].get('voice') != None:    # 若存在声音则播放
				self.voice_channel.play(pygame.mixer.Sound(MUSIC_PATH + self.dialogue[self.dialogue_index].get('voice')))
			
			
		else:                      # 对话播放完了，判断是否还存在下一个json，如果有就继续，没有就回到主界面
			if self.json_data.get('next') == None:
				self.end = True
			else:
				self.json = self.json_data['next']     # 尝试调取下一个json
				self.load_scene()      # 加载新的json
				self.load_dialogue()    # 加载第一个对话
	
# 关键的文字类，用于整合文字动画和各种效果
class Mz1Text:
	def __init__(self, s, x, y, color=(0,0,0), text_size=int(WIDTH / 42), font=None):
		# 参数：字符串， x,y,颜色（默认黑色）,文字大小(默认为dialoguebox中的文字大小), 字体
		self.s = s
		self.x = x
		self.y = y
		self.color = color
		# 生成字体
		self.text_size = text_size
		if font == None:   # 没有传递参数的默认情况
			self.font = pygame.font.Font(FONT_PATH+'a.ttf', self.text_size) # 生成一个字体
		else:
			self.font = font
			
		self.text = self.font.render(self.s, True, self.color)  # 根据颜色渲染字符串渲染成Surface
		self.width, self.height = self.font.size(self.s)                  # 获取长宽
		self.show_area = pygame.Rect(0, 0, 0, self.height)     # 显示区域，用于逐字显示
		self.show_length = 0            # 用于控制动画
			
	def update(self):
		# 逐渐从左向右显示文字
		if self.show_length < self.width:
			self.show_length += WIDTH // 50       # 增加显示长度
			self.show_area.update(0, 0, self.show_length, self.height)
		
	def show(self, screen):
		screen.blit(self.text, (self.x, self.y), area=self.show_area)          # 显示字符串
	
	
# 菜单类，用于处理各种点击事件
# 应用于开始菜单和游戏过程中菜单
class Mz1Menu:
	def __init__(self):
		# 初始化
		# 图片
		self.open_img = Mz1Img("sys/menu_background.jpg", 0, 0, WIDTH, HEIGHT, alpha_rate=2)    # 加载开场图片
		
		# 音效
		self.open_bgm = pygame.mixer.Sound(MUSIC_PATH+'bgm_menu.mp3')
		self.bgm_channel = pygame.mixer.Channel(0)                  # 获取一个音频通道   0作为bgm通道  1作为人声音通道
		
		self.bgm_channel.play(self.open_bgm, loops=-1)         # 无限循环播放 开场bgm

		# 按钮
		self.btn_new_start = Mz1Button("新游戏", WIDTH * 0.43, HEIGHT * 0.54)
		self.btn_load = Mz1Button("读取存档", WIDTH * 0.43, HEIGHT * 0.61)
		self.btn_special = Mz1Button("特别模式", WIDTH * 0.43, HEIGHT * 0.68)
		self.btn_setting = Mz1Button("设  置", WIDTH * 0.43, HEIGHT * 0.75)
		self.btn_exit = Mz1Button("退出游戏", WIDTH * 0.43, HEIGHT * 0.82)
		self.btn_about = Mz1Button("ABOUT", WIDTH * 0.43, HEIGHT * 0.89, common_color=(0xF9,0x70,0xFF))
		
		# 设置对话框
		self.setting_dialogue = None    # 初始为空
		
		
	def update(self):
		# 更新内容和动画
		self.open_img.update()
		
	def show(self, screen):
		# 显示菜单内容
		screen.fill((0,0,0))         # 填充黑色背景
		self.open_img.show(screen)      # 显示开场图片

		self.btn_new_start.show(screen)        # 渲染按钮
		self.btn_load.show(screen)        # 渲染按钮
		self.btn_special.show(screen)
		self.btn_setting.show(screen)
		self.btn_exit.show(screen)            # 渲染按钮
		self.btn_about.show(screen)          # 渲染按钮
		
		if self.setting_dialogue != None:     # 创建了菜单就画出来
			self.setting_dialogue.show(screen)		
		
	def on_click(self, event):
		# 响应点击事件  返回指令
		# 如果打开了设置菜单，直接将点击交给菜单去处理
		if self.setting_dialogue != None:     # 如果创建了菜单
			cmd = self.setting_dialogue.on_click(event)   # 交给菜单去处理
			if cmd == 'close':   # 关闭菜单
				self.setting_dialogue = None
			return None
		_new_start = self.btn_new_start.on_click()
		_load = self.btn_load.on_click()
		_special = self.btn_special.on_click()
		_setting = self.btn_setting.on_click()
		_exit = self.btn_exit.on_click()
		_about = self.btn_about.on_click()

		if _exit:                       # 退出按钮被点击
			print("退出游戏")
			ans = messagebox.askokcancel("TIP", "确认要退出吗？")
			if ans == True:     # 确认要退出
				exit_game()
		if _new_start:                  # 新游戏按钮被点击
			print('开始新游戏')
			return 'new game'
		if _load:                # 继续游戏
			print('继续游戏')
			messagebox.showinfo('TIP','这个功能还没有做好哦') 
		if _special:                # 特别模式(CG, 场景回顾, 音乐?)
			print('特别模式')
			messagebox.showinfo('TIP','这个功能还没有做好哦') 
			var_string = simpledialog.askstring(title ="请输入", prompt = "提示：")  # 测试文本输入控件
			print(f"用户输入:{var_string}")   # 测试文本输入控件
		if _setting:     #设置
			
			self.setting_dialogue = Mz1SettingDialogue()   # 创建一个设置菜单对话
			
		if _about:
			messagebox.showinfo('ABOUT', "author: Mz1\r\nemail:mzi_mzi@163.com\r\nhttps://egg34.gitee.io")


# 欢迎界面模块，用于显示图片然后加载东西什么的
class Mz1Welcome:
	def __init__(self):
		self.img_index = 0   # 标记现在播放的图片张数
		self.img_list = []
		self.img_list.append(Mz1Img("welcome2.png", 0, 0, WIDTH, HEIGHT, alpha_rate=2))
		self.img_list.append(Mz1Img("welcome.jpg", 0, 0, WIDTH, HEIGHT, alpha_rate=2))  # 添加开场图片
			
		self.img_count = len(self.img_list)   # 存在几张开场图片
		self.img = self.img_list[self.img_index]    # 取图片
		self.end = False
		
	def update(self):   # 更新动画
		self.img.update()
		if self.img.alpha >= self.img.alpha_max:
			self.img.fade_out()   # 使图片淡出
		if self.img.alpha <= 0:   # 淡出完成
			self.img_index += 1    # 计算张数增加
			if self.img_index >= self.img_count:
				self.end = True
			else:
				self.img = self.img_list[self.img_index]    # 取图片
			
	def show(self, screen):   # 显示
		screen.fill((0,0,0)) #黑屏
		self.img.show(screen)
		
	def on_click(self):
		# 点击事件处理
		# 设置透明度为-100，跳过一张欢迎动画
		self.img.alpha = -100

	def is_end(self):    # 用于判断是否结束
		return self.end


# 对话框，用于实现设置/存档功能
class Mz1SettingDialogue:
	def __init__(self):
		print('打开设置对话框')
		# 定义大小
		self.width = WIDTH * 0.8
		self.height = HEIGHT * 0.8
		self.x = WIDTH * 0.1
		self.y = HEIGHT * 0.1
		self.rect = pygame.Rect(self.x, self.y, self.width, self.height)   # 创建自身的rect对象
		
	def show(self, screen):
		# 显示模块
		pygame.draw.rect(screen, (255,171,171), self.rect)   # 画一个大框
		
	def update(self):
		# 动画处理
		pass
	
	def on_click(self, event):
		# 点击处理
		x, y = event.pos   # 获取点击位置
		print(f' > 设置对话框被点击 - {x},{y}')
		if not self.rect.collidepoint((x, y)):    # 看看是不是点中了
			# 没点中，关闭对话框
			return 'close'
		return None

# 总的音乐控制类
class Mz1MusicController:
	def __init__(self):
		print("> 创建音乐控制台")


###############################################################################################
# 主要游戏类，直接加载这个类就可以了
#
class Ezgal:
	def __init__(self):
		pygame.init()        # 初始化pygame
		
		# 自定义计时事件
		self.COUNT = pygame.USEREVENT +1
		# 每隔20ms发送一次自定义事件, 用于游戏计时
		pygame.time.set_timer(self.COUNT, 20)

		# 窗口基础设定
		self.screen = pygame.display.set_mode((WIDTH, HEIGHT))   # 设置并获取窗口
		pygame.display.set_caption(TITLE)    # 设置标题
		icon = pygame.image.load(ICON_PATH+ICON)    # 加载图标
		pygame.display.set_icon(icon)   # 设置图标
		
		self.font = pygame.font.Font(FONT_PATH+'a.ttf', int(WIDTH / 64)) # 生成一个字体
		
		self.x = 0     # 测试用的坐标
		
		self.welcome = Mz1Welcome()   # 创建一个欢迎界面
		
		# 游戏功能变量
		self.status = 'welcome'           # 游戏的状态 welcome, menu, game

	def update(self):
		# 根据计时器更新游戏数据
		if self.status == 'welcome':    # 在欢迎模式下
			self.welcome.update()
			if self.welcome.is_end():   # 动画结束
				self.status = 'menu'   # 进入菜单
				del self.welcome
				self.menu = Mz1Menu()
		elif self.status == 'menu':    # 在菜单模式下
			self.menu.update()
			self.x += 1   # 测试用
		elif self.status == 'game':    # 在游戏模式下
			self.game.update()      # 调用游戏面板的更新函数
			if self.game.is_end():
				self.status = 'menu'          # 游戏结束返回菜单
				del self.game         # 测试：清理游戏模块
				self.menu = Mz1Menu()    # 测试：重新创建菜单
				self.x = 0       # 测试用，重置位置

	def show(self):
		# 刷新显示画面
		# 判断游戏状态进行显示
		if self.status == 'welcome':    # 欢迎界面
			self.welcome.show(self.screen)
			
		elif self.status == 'menu':  # 菜单模式
			self.menu.show(self.screen)    # 显示菜单
			self.show_text('Mz1测试', self.x,0)        # 显示测试字符串
			
		elif self.status == 'game':  # 游戏模式
			self.game.show(self.screen)            # 渲染游戏画面
		
		pygame.display.flip()    # 刷新画面

		


	def show_text(self, s, x, y):
		# 显示字符串(字符串， x坐标， y坐标)
		text = self.font.render(s, True, (0, 0, 0))  # 黑色字符串
		self.screen.blit(text, (x,y))
		
		

	# 运行游戏
	def run(self):
		# 先进入菜单选择模块
		while True:            # 整个游戏的事件循环
			for event in pygame.event.get():
				if event.type == pygame.QUIT:     # 退出游戏
					print("退出游戏")
					exit_game()
				if event.type == self.COUNT:
					self.update()     # 游戏刷新
					
				if event.type == pygame.MOUSEBUTTONDOWN:      # 点击事件
					if self.status == 'welcome':  # 在欢迎界面下
						self.welcome.on_click()                      # 点击跳过开场动画						
					elif self.status == 'menu':   # 在menu状态下
						cmd = self.menu.on_click(event)    # 让菜单响应点击事件  退出或者开始新游戏	
						if cmd == 'new game':   # 获得新游戏指令
							del self.menu     # 测试：清理menu
							self.game = Mz1Game()       # 初始化游戏 这里考虑用一个新的线程来做，这样可以同时加载动画
							self.status = 'game'				
							
					elif self.status == 'game':  # 在游戏状态下
						self.game.on_click(event)       # 让游戏面板去响应点击事件

			self.show()	   # 画面刷新
			

	
if __name__ == "__main__":
	ezgal = Ezgal()
	ezgal.run()
